package demoMod.invincibleOne.ui;

import basemod.BaseMod;
import basemod.abstracts.CustomSavable;
import com.badlogic.gdx.math.Vector2;
import com.google.gson.reflect.TypeToken;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.utility.UseCardAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.powers.AbstractPower;
import com.megacrit.cardcrawl.powers.StrengthPower;
import com.megacrit.cardcrawl.relics.AbstractRelic;
import com.megacrit.cardcrawl.rewards.RewardItem;
import com.megacrit.cardcrawl.rooms.*;
import demoMod.invincibleOne.InvincibleOneMod;
import demoMod.invincibleOne.enums.CardTagsEnum;
import demoMod.invincibleOne.interfaces.IOnCompleteBingo;
import demoMod.invincibleOne.relics.TaskGoal;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public enum BingoItem implements CustomSavable<List<Integer>> {
    LASTS_3_TURNS(0, 3) {
        boolean isMonsterRoom;

        @Override
        public void atBattleStart() {
            isMonsterRoom = AbstractDungeon.getCurrRoom().getClass().getSimpleName().equals(MonsterRoom.class.getSimpleName());
        }

        @Override
        public void atTurnStart() {
            if (isMonsterRoom) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }

        @Override
        public void onVictory() {
            if (status == 0) {
                currentProgress = 0;
            }
        }
    },
    DEALS_ANY_DAMAGE(0) {
        @Override
        public int onAttacked(DamageInfo info, int damageAmount) {
            if (damageAmount > 0 && AbstractDungeon.getCurrRoom().getClass().getSimpleName().equals(MonsterRoom.class.getSimpleName())) {
                completeBingo();
            }
            return damageAmount;
        }
    },
    HP_REACHES_100(0) {
        private boolean activated;

        @Override
        public void atBattleStart() {
            activated = true;
        }

        @Override
        public void update() {
            if (activated && AbstractDungeon.getCurrMapNode() != null
                    && AbstractDungeon.getCurrRoom() != null
                    && AbstractDungeon.getMonsters() != null
                    && AbstractDungeon.getMonsters().monsters != null
                    && AbstractDungeon.getCurrRoom().getClass().getSimpleName().equals(MonsterRoom.class.getSimpleName())
            ) {
                InvincibleOneMod.addToBot(new AbstractGameAction() {
                    @Override
                    public void update() {
                        if (AbstractDungeon.getCurrMapNode() == null) {
                            isDone = true;
                            return;
                        }
                        for (AbstractMonster m : AbstractDungeon.getMonsters().monsters.stream().filter(mo -> !mo.isDeadOrEscaped()).collect(Collectors.toList())) {
                            if (m.currentHealth >= 100) {
                                completeBingo();
                                activated = false;
                                break;
                            }
                        }
                        isDone = true;
                    }
                });
            }
        }

        @Override
        public void onVictory() {
            activated = false;
        }
    },
    BLOCK_REACHES_20(0) {
        private boolean activated;

        @Override
        public void atBattleStart() {
            activated = true;
        }

        @Override
        public void update() {
            if (activated && AbstractDungeon.getCurrMapNode() != null && AbstractDungeon.getMonsters() != null) {
                for (AbstractMonster m : AbstractDungeon.getMonsters().monsters.stream().filter(mo -> !mo.isDeadOrEscaped()).collect(Collectors.toList())) {
                    if (m.currentBlock >= 20) {
                        completeBingo();
                        activated = false;
                        break;
                    }
                }
            }
        }

        @Override
        public void onVictory() {
            activated = false;
        }
    },
//    HEALED_REACHES_10(0, 10) {
//        @Override
//        public void atBattleStart() {
//            currentProgress = 0;
//        }
//
//        @Override
//        public void onMonsterHeal(AbstractMonster m, int healAmount) {
//            currentProgress += healAmount;
//            if (currentProgress >= totalProgress) {
//                currentProgress = totalProgress;
//                completeBingo();
//            }
//        }
//    },
    HAND_IS_FULL(0) {
        private boolean activated;

        @Override
        public void atBattleStart() {
            activated = true;
        }

        @Override
        public void update() {
            if (activated) {
                if (AbstractDungeon.player.hand.size() >= BaseMod.MAX_HAND_SIZE) {
                    activated = false;
                    completeBingo();
                }
            }
        }

        @Override
        public void onVictory() {
            activated = false;
        }
    },
    STREAK_4(0, 4) {
        boolean flag;

        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (!flag && status != 1) {
                currentProgress = 0;
            }
            flag = false;
        }

        @Override
        public void onVictory() {
            flag = true;
            currentProgress++;
            if (currentProgress >= totalProgress) {
                currentProgress = totalProgress;
                completeBingo();
            }
        }
    },
    SLEEP_ONCE(0, 1) {
        @Override
        public void onRest() {
            currentProgress++;
            if (currentProgress >= totalProgress) {
                currentProgress = totalProgress;
                completeBingo();
            }
        }
    },
    ENTER_4_EVENT_ROOMS(0, 4) {
        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (room instanceof EventRoom) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    ENTER_2_SHOP_ROOMS(0, 2) {
        boolean flag;

        @Override
        public void onEnterRoom(AbstractRoom room) {
            flag = true;
        }

        @Override
        public void update() {
            if (AbstractDungeon.screen == AbstractDungeon.CurrentScreen.SHOP && flag) {
                flag = false;
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }

        @Override
        public void reset() {
            super.reset();
            flag = false;
        }
    },
    FIGHT_3_ELITES(0, 3) {
        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (room instanceof MonsterRoomElite) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    GAIN_500_GOLDS(0) {
        @Override
        public void update() {
            if (status != 1 && AbstractDungeon.player != null && AbstractDungeon.player.gold >= 450) {
                completeBingo();
            }
        }
    },
    DONT_ENTER_SHOP_IN_ACT_1(0) {
        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (status == 0 && AbstractDungeon.actNum > 1) {
                completeBingo();
            }
        }

        @Override
        public void onSpendGold() {
            if (AbstractDungeon.actNum == 1 && status == 0) {
                failBingo();
            }
        }
    },
    DECREASE_PLAYER_HP_TO_110_OR_LOWER(0) {
        @Override
        public int onAttacked(DamageInfo info, int damageAmount) {
            if (status == 0) {
                InvincibleOneMod.addToBot(new AbstractGameAction() {
                    @Override
                    public void update() {
                        if ((float)AbstractDungeon.player.currentHealth <= (float)AbstractDungeon.player.maxHealth * 0.8F) {
                            completeBingo();
                        }
                        isDone = true;
                    }
                });
            }
            return super.onAttacked(info, damageAmount);
        }
    },
    ADD_8_COMMON_CARDS(0, 8) {
        @Override
        public void onObtainCard(AbstractCard card) {
            if (status == 0) {
                if (card.rarity == AbstractCard.CardRarity.COMMON) {
                    currentProgress++;
                }
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    ADD_6_UNCOMMON_CARDS(0, 6) {
        @Override
        public void onObtainCard(AbstractCard card) {
            if (status == 0) {
                if (card.rarity == AbstractCard.CardRarity.UNCOMMON) {
                    currentProgress++;
                }
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    ADD_4_CURSES(0, 4) {
        @Override
        public void onObtainCard(AbstractCard card) {
            if (status == 0) {
                if (card.rarity == AbstractCard.CardRarity.CURSE || card.hasTag(CardTagsEnum.VIRUS)) {
                    currentProgress++;
                }
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    COMPLETE_ANY_2_SIBLING_BINGO_BEFORE_ACT_3(0, 2) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status != 0) {
                return;
            }
            Vector2 axis = BingoPanel.getCoordinatesBySlot(bingoItem.currentSlot);
            Vector2 myAxis = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            axis.sub(myAxis);
            if (axis.dst2(new Vector2(0, 0)) <= 1) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }

        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (AbstractDungeon.actNum >= 3 && status == 0) {
                failBingo();
            }
        }
    },
    PLAY_2_OR_LESS_CARDS_IN_A_SINGLE_TURN(1) {
        int count;

        @Override
        public void atTurnStart() {
            count = 0;
        }

        @Override
        public void onPlayerEndTurn() {
            if (count <= 3 && status == 0) {
                completeBingo();
            }
        }

        @Override
        public void onUseCard(AbstractCard card, UseCardAction action) {
            count++;
        }
    },
    LASTS_5_TURNS(1, 5){
        boolean isMonsterRoom;

        @Override
        public void atBattleStart() {
            isMonsterRoom = AbstractDungeon.getCurrRoom().getClass().getSimpleName().equals(MonsterRoom.class.getSimpleName());
        }

        @Override
        public void atTurnStart() {
            if (isMonsterRoom) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }

        @Override
        public void onVictory() {
            if (status == 0) {
                currentProgress = 0;
            }
        }
    },
    BLOCK_REACHES_50(1) {
        private boolean activated;

        @Override
        public void atBattleStart() {
            activated = true;
        }

        @Override
        public void update() {
            if (activated && AbstractDungeon.getCurrMapNode() != null && AbstractDungeon.getMonsters() != null) {
                for (AbstractMonster m : AbstractDungeon.getMonsters().monsters.stream().filter(mo -> !mo.isDeadOrEscaped()).collect(Collectors.toList())) {
                    if (m.currentBlock >= 50) {
                        completeBingo();
                        activated = false;
                        break;
                    }
                }
            }
        }

        @Override
        public void onVictory() {
            activated = false;
        }
    },
    DEAL_20_DAMAGE_IN_A_SINGLE_TURN(1) {
        int count;

        @Override
        public void atTurnStart() {
            count = 0;
        }

        @Override
        public int onAttacked(DamageInfo info, int damageAmount) {
            if (status == 0) {
                count += damageAmount;
                if (count >= 20) {
                    completeBingo();
                }
            }
            return super.onAttacked(info, damageAmount);
        }
    },
    STRENGTH_MATCHES_10(1) {
        @Override
        public void update() {
            if (AbstractDungeon.getCurrMapNode() != null
                    && AbstractDungeon.getCurrRoom() != null
                    && AbstractDungeon.getCurrRoom().monsters != null
                    && AbstractDungeon.getCurrRoom().monsters.monsters != null
                    && status == 0
                    && AbstractDungeon.getCurrRoom().getClass().getSimpleName().equals(MonsterRoom.class.getSimpleName())
            ) {
                for (AbstractMonster mo : AbstractDungeon.getCurrRoom().monsters.monsters.stream().filter(m -> !m.isDeadOrEscaped()).collect(Collectors.toList())) {
                    if (mo.hasPower(StrengthPower.POWER_ID) && mo.getPower(StrengthPower.POWER_ID).amount >= 10) {
                        completeBingo();
                    }
                }
            }
        }
    },
//    SLEEP_3_TIMES(1, 3) {
//        @Override
//        public void onRest() {
//            currentProgress++;
//            if (currentProgress >= totalProgress) {
//                currentProgress = totalProgress;
//                completeBingo();
//            }
//        }
//    },
    FIGHT_5_ELITES(1, 5) {
        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (room instanceof MonsterRoomElite) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    ENTER_8_EVENT_ROOMS(1, 7) {
        @Override
        public void onEnterRoom(AbstractRoom room) {
            if (room instanceof EventRoom) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
    CLEAR_A_SHOP(1) {
        @Override
        public void onSpendGold() {
            InvincibleOneMod.addToBot(new AbstractGameAction() {
                @Override
                public void update() {
                    if (AbstractDungeon.shopScreen.coloredCards.isEmpty() && !AbstractDungeon.shopScreen.purgeAvailable) {
                        completeBingo();
                    }
                    isDone = true;
                }
            });
        }
    },
//    DONT_REMOVE_CARDS_IN_SHOP_BEFORE_ACT_3(1) {
//        @Override
//        public void onEnterRoom(AbstractRoom room) {
//            if (status == 0 && AbstractDungeon.actNum >= 3) {
//                completeBingo();
//            }
//        }
//
//        @Override
//        public void onRemoveCard(AbstractCard card) {
//            if (AbstractDungeon.actNum < 3 && status == 0 && AbstractDungeon.getCurrRoom() instanceof ShopRoom) {
//                failBingo();
//            }
//        }
//    },
    DECREASE_PLAYER_HP_TO_90_OR_LOWER(1) {
        @Override
        public int onAttacked(DamageInfo info, int damageAmount) {
            InvincibleOneMod.addToBot(new AbstractGameAction() {
                @Override
                public void update() {
                    if ((float)AbstractDungeon.player.currentHealth <= (float)AbstractDungeon.player.maxHealth * 0.6F) {
                        completeBingo();
                    }
                    isDone = true;
                }
            });
            return super.onAttacked(info, damageAmount);
        }
    },
    GAIN_12_NEGATIVE_POWERS_IN_A_COMBAT(1) {
        @Override
        public void update() {
            if (AbstractDungeon.getCurrMapNode() != null && AbstractDungeon.getCurrRoom() != null && AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT && status == 0) {
                AbstractPlayer p = AbstractDungeon.player;
                int sum = 0;
                for (AbstractPower power : p.powers) {
                    if (power.type == AbstractPower.PowerType.DEBUFF) {
                        sum += power.amount > 0 ? power.amount : 1;
                    }
                }
                if (sum >= 12) {
                    completeBingo();
                }
            }
        }
    },
    GAIN_5_NEGATIVE_POWERS_IN_A_COMBAT(0) {
        @Override
        public void update() {
            if (AbstractDungeon.getCurrMapNode() != null && AbstractDungeon.getCurrRoom() != null && AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT && status == 0) {
                AbstractPlayer p = AbstractDungeon.player;
                int sum = 0;
                for (AbstractPower power : p.powers) {
                    if (power.type == AbstractPower.PowerType.DEBUFF) {
                        sum += power.amount > 0 ? power.amount : 1;
                    }
                }
                if (sum >= 5) {
                    completeBingo();
                }
            }
        }
    },
    ADD_14_COMMON_CARDS(1, 14) {
        @Override
        public void onObtainCard(AbstractCard card) {
            if (status == 0 && card.rarity == AbstractCard.CardRarity.COMMON) {
                currentProgress++;
                if (currentProgress >= totalProgress) {
                    currentProgress = totalProgress;
                    completeBingo();
                }
            }
        }
    },
//    ADD_NO_COMMON_CARDS_BEFORE_ACT_2(1) {
//        @Override
//        public void onEnterRoom(AbstractRoom room) {
//            if (status == 0 && AbstractDungeon.actNum >= 2) {
//                completeBingo();
//            }
//        }
//
//        @Override
//        public void onObtainCard(AbstractCard card) {
//            if (card.rarity == AbstractCard.CardRarity.RARE && AbstractDungeon.actNum < 2 && status == 0) {
//                failBingo();
//            }
//        }
//    },
    SKIP_2_CARDS(0, 2) {
        @Override
        public void onLeaveRoom(AbstractRoom currRoom) {
            if (status == 1) {
                return;
            }
            int count = (int) AbstractDungeon.combatRewardScreen.rewards.stream().filter(rewardItem -> rewardItem.type == RewardItem.RewardType.CARD).count();
            currentProgress += count;
            if (currentProgress >= totalProgress) {
                currentProgress = totalProgress;
                completeBingo();
            }
        }
    },
//    SKIP_5_CARDS(1, 5) {
//        @Override
//        public void onLeaveRoom(AbstractRoom currRoom) {
//            if (status == 1) {
//                return;
//            }
//            int count = (int) AbstractDungeon.combatRewardScreen.rewards.stream().filter(rewardItem -> rewardItem.type == RewardItem.RewardType.CARD).count();
//            currentProgress += count;
//            if (currentProgress >= totalProgress) {
//                currentProgress = totalProgress;
//                completeBingo();
//            }
//        }
//    },
    REWARD_BLANK(3, true),
    REWARD_CARD_A(2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.LINES - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates(i, (int) pos.y));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                // 告诉玩家有奖励可以领
                TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                InvincibleOneMod.bingoRewardList.hide = false;
                tg.beginLongPulse();
                CardCrawlGame.sound.play("UNLOCK_PING");

                completeBingo();
                InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.cardRewardBuilder.build());
            }
        }
    },
    REWARD_CARD_B(2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.LINES - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates(i, (int) pos.y));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                InvincibleOneMod.bingoRewardList.hide = false;
                tg.beginLongPulse();
                CardCrawlGame.sound.play("UNLOCK_PING");

                completeBingo();
                InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.cardRewardBuilder.build());
            }
        }
    },
    REWARD_LEGEND_CARD_A(2, 2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.COLUMNS - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x, i));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                currentProgress = 1;
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x + 1, (int) pos.y));
                if (slot.getBingoItem().status == 1) {
                    InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.legendaryCardRewardBuilder.build());
                    currentProgress = 2;
                    completeBingo();

                    TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                    InvincibleOneMod.bingoRewardList.hide = false;
                    tg.beginLongPulse();
                    CardCrawlGame.sound.play("UNLOCK_PING");
                } else if (slot.getBingoItem().currentProgress == 1) {
                    status = 1;
                    currentProgress = 2;
                    slot.getBingoItem().onBingoComplete(this);
                }
            }
        }
    },
    REWARD_LEGEND_CARD_B(2, 2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.COLUMNS - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x, i));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                currentProgress = 1;
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x - 1, (int) pos.y));
                if (slot.getBingoItem().status == 1) {
                    InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.legendaryCardRewardBuilder.build());
                    currentProgress = 2;
                    completeBingo();

                    TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                    InvincibleOneMod.bingoRewardList.hide = false;
                    tg.beginLongPulse();
                    CardCrawlGame.sound.play("UNLOCK_PING");
                } else if (slot.getBingoItem().currentProgress == 1) {
                    status = 1;
                    currentProgress = 2;
                    slot.getBingoItem().onBingoComplete(this);
                }
            }
        }
    },
    REWARD_LEGEND_CARD_C(2, 2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.COLUMNS - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x, i));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                currentProgress = 1;
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x + 1, (int) pos.y));
                if (slot.getBingoItem().status == 1) {
                    InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.legendaryCardRewardBuilder.build());
                    currentProgress = 2;
                    completeBingo();

                    TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                    InvincibleOneMod.bingoRewardList.hide = false;
                    tg.beginLongPulse();
                    CardCrawlGame.sound.play("UNLOCK_PING");
                } else if (slot.getBingoItem().currentProgress == 1) {
                    status = 1;
                    currentProgress = 2;
                    slot.getBingoItem().onBingoComplete(this);
                }
            }
        }
    },
    REWARD_LEGEND_CARD_D(2, 2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.COLUMNS - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x, i));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                currentProgress = 1;
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates((int) pos.x - 1, (int) pos.y));
                if (slot.getBingoItem().status == 1) {
                    InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.legendaryCardRewardBuilder.build());
                    currentProgress = 2;
                    completeBingo();

                    TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                    InvincibleOneMod.bingoRewardList.hide = false;
                    tg.beginLongPulse();
                    CardCrawlGame.sound.play("UNLOCK_PING");
                } else if (slot.getBingoItem().currentProgress == 1) {
                    status = 1;
                    currentProgress = 2;
                    slot.getBingoItem().onBingoComplete(this);
                }
            }
        }
    },
    REWARD_CARD_REMOVAL_A(2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.LINES - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates(i, (int) pos.y));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                completeBingo();
                InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.cardRemovalRewardBuilder.build());

                TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                InvincibleOneMod.bingoRewardList.hide = false;
                tg.beginLongPulse();
                CardCrawlGame.sound.play("UNLOCK_PING");
            }
        }
    },
    REWARD_CARD_REMOVAL_B(2, true) {
        @Override
        public void onBingoComplete(BingoItem bingoItem) {
            if (status == 1) {
                return;
            }
            Vector2 pos = BingoPanel.getCoordinatesBySlot(getCurrentSlot());
            boolean complete = true;
            for (int i=0;i<BingoPanel.LINES - 1;i++) {
                BingoSlot slot = InvincibleOneMod.bingoPanel.getSlots().get(BingoPanel.getIndexByCoordinates(i, (int) pos.y));
                if (slot.getBingoItem().status != 1) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                completeBingo();
                InvincibleOneMod.bingoRewardList.getRewards().add(BingoRewardItem.cardRemovalRewardBuilder.build());

                TaskGoal tg = (TaskGoal)AbstractDungeon.player.getRelic(TaskGoal.ID);
                InvincibleOneMod.bingoRewardList.hide = false;
                tg.beginLongPulse();
                CardCrawlGame.sound.play("UNLOCK_PING");
            }
        }
    }
    ;

    protected final boolean isReward;
    private final int difficulty;
    private final String description;

    protected int currentProgress;
    protected final int totalProgress;

    protected int status; //0未达成 1已达成 2本局已无法达成

    private BingoSlot currentSlot;

    BingoItem(int difficulty) {
        this(difficulty, 0);
    }

    BingoItem(int difficulty, int progress) {
        this(difficulty, progress, false);
    }

    BingoItem(int difficulty, boolean isReward) {
        this(difficulty, 0, isReward);
    }

    BingoItem(int difficulty, int progress, boolean isReward) {
        this.difficulty = difficulty;
        this.description = CardCrawlGame.languagePack.getUIString(InvincibleOneMod.makeID(name())).TEXT[0];
        BaseMod.addSaveField(name(), this);
        this.currentProgress = 0;
        this.totalProgress = progress;
        this.isReward = isReward;
    }

    public void atBattleStart() {}

    public void atTurnStart() {}

    public void onPlayerEndTurn() {}

    public void onVictory() {}

    public void update() {}

    public void onMonsterHeal(AbstractMonster m, int healAmount) {}

    public void onEnterRoom(AbstractRoom room) {}

    public void onLeaveRoom(AbstractRoom currRoom) {}

    public void onObtainCard(AbstractCard card) {}

    public int onAttacked(DamageInfo info, int damageAmount) {
        return damageAmount;
    }

    public void onUseCard(AbstractCard card, UseCardAction action) {}

    public void onRemoveCard(AbstractCard card) {}

    public void onRest() {}

    public void onSpendGold() {}

    public void reset() {
        this.status = 0;
        this.currentProgress = 0;
    }

    public void completeBingo() {
        if (this.status != 0) {
            return;
        }
        this.status = 1;
        this.currentProgress = this.totalProgress;
        InvincibleOneMod.bingoPanel.getSlots().forEach(slot -> {
            if (slot != currentSlot) {
                slot.getBingoItem().onBingoComplete(this);
            }
        });
        for (AbstractRelic r: AbstractDungeon.player.relics) {
            if (r instanceof IOnCompleteBingo) {
                ((IOnCompleteBingo) r).onCompleteBingo();
            }
        }
    }

    public void failBingo() {
        if (status == 2) {
            return;
        }
        CardCrawlGame.sound.playA("SFX_BINGO_FAILED", 0.0F);
        status = 2;
    }

    public void onBingoComplete(BingoItem bingoItem) {}

    public int getDifficulty() {
        return difficulty;
    }

    public String getDescription() {
        if (totalProgress > 0) {
            return String.format(description, currentProgress, totalProgress);
        }
        return description;
    }

    public int getStatus() {
        return status;
    }

    public BingoSlot getCurrentSlot() {
        return currentSlot;
    }

    public void setCurrentSlot(BingoSlot currentSlot) {
        this.currentSlot = currentSlot;
    }

    @Override
    public List<Integer> onSave() {
        return Arrays.asList(this.status, this.currentProgress);
    }

    @Override
    public void onLoad(List<Integer> data) {
        if (data != null) {
            this.status = data.get(0);
            this.currentProgress = data.get(1);
        }
    }

    @Override
    public Type savedType() {
        return new TypeToken<List<Integer>>(){}.getType();
    }
}
